<template>
  <div class="containers">
    <div class="tools_" >
      <a-icon type="save" @click="saveClick" title="保存" />
      <a-icon type="folder-open" @click="uploadClick" title="打开本地bpmn" />
      <input v-show="false" id="id_face_file" @change="uploadFile1" ref="files1" type="file">
      <a ref="saveDiagram" href="javascript:" title="保存为bpmn"> <a-icon type="download"/></a>
      <a-divider type="vertical" />
      <a-icon type="zoom-in" title="放大" @click="handleZoomIn" />
      <a-icon type="zoom-out" title="缩小" @click="handleZoomOut" />
      <a-divider type="vertical" />
      <a-icon type="double-left" title="撤销" @click="handleUndo" />
      <a-icon type="double-right" title="恢复" @click="handleRedo" />
    </div>
    <div class="canvas" ref="canvas"></div>
    <div id="js-properties-panel" class="panel"></div>
    <j-select-user-by-dep-modal ref="selectModal" :modal-width="1250" :multi="true" @ok="selectOK" :user-ids="userSelect.value"/>
    <j-select-biz-component-modal
      v-model="roleSettings.selectValue"
      :visible.sync="roleSettings.visible"
      v-bind="modalProps"
      :listUrl="roleSettings.listUrl"
      :columns="roleSettings.columns"
      @ok="roleSelectOk"
    />
    <!-- <j-select-biz-component-modal
      v-model="positionSettings.positionSelectValue"
      :visible.sync="positionSettings.positionVisible"
      v-bind="positionModalProps"
      :listUrl="positionSettings.listUrl"
      :columns="positionSettings.columns"
      @ok="roleSelectOk"
    /> -->
    <PositionAndExpressionModal ref="PositionAndExpressionModal" @ok="selectPositionAndExpression" />
    <!-- <ext-act-expression-list-modal ref="ExtActExpressionListModal"></ext-act-expression-list-modal> -->
  </div>
</template>

<script>
// 引入相关的依赖
import BpmnModeler from 'bpmn-js/lib/Modeler'

// import propertiesPanelModule from 'bpmn-js-properties-panel'
// import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'

import propertiesPanelModule from './workflow/properties-panel'// properties-panel自定义修改了一部分
import propertiesProviderModule from './workflow/properties-panel/provider/activiti' // 兼容activiti

// 以下为bpmn工作流绘图工具的样式
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'

import JSelectUserByDepModal from '@/components/jeecgbiz/modal/JSelectUserByDepModal'// 用户弹窗
import JSelectBizComponentModal from '@/components/jeecgbiz/JSelectBizComponent/JSelectBizComponentModal'// 用户弹窗
// import ExtActExpressionListModal from './modal/ExtActExpressionListModal'//表达式弹窗
import PositionAndExpressionModal from './modal/PositionAndExpressionModal'// 职务弹窗

import customTranslate from './workflow/customTranslate/customTranslate'
import activitiModdleDescriptor from './activiti.json'// 修改属性前缀
// import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import { newDiagram } from './newDiagram'
var customTranslateModule = {
  translate: ['value', customTranslate]
}

export default {
  name: 'bpmn',
  props: {
    xml: {
      type: String,
      default: ''
    },
    processData: {
      type: Object,
      default: () => {}
    }
  },
  components: {
    JSelectUserByDepModal,
    JSelectBizComponentModal,
    PositionAndExpressionModal
    // ExtActExpressionListModal
  },
  data() {
    return {
      // bpmn建模器
      bpmnModeler: null,
      container: null,
      canvas: null,
      defaultXmlStr: newDiagram,
      zoom: 1,
      userSelect: {
        value: '',
        key: ''
      },
      shape: '',
      // returnId: false, // 是否返回 id，默认 false，返回 code
      multiple: true,
      headers: {
        authorization: 'authorization-text'
      },
      roleSettings: {
        selectValue: [],
        listUrl: '/sys/role/list',
        visible: false,
        returnKeys: ['id', 'roleCode'],
        displayKey: null,
        columns: [
          { title: '角色名称', dataIndex: 'roleName', align: 'center', width: 120 },
          { title: '角色编码', dataIndex: 'roleCode', align: 'center', width: 120 }
        ]
      },
      uploadFile: null,
      positionSettings: {// 职务弹窗配置
        positionSelectValue: [], // 职务选择值
        positionVisible: false, // 职务弹窗显示
        name: '职务',
        displayKey: 'name',
        returnKeys: ['id', 'code'],
        listUrl: '/sys/position/list',
        queryParamCode: 'name',
        queryParamText: '职务名称',
        columns: [
          { title: '职务名称', dataIndex: 'name', align: 'center', width: '30%', widthRight: '70%' },
          { title: '职务编码', dataIndex: 'code', align: 'center', width: '35%' },
          { title: '职级', dataIndex: 'rank_dictText', align: 'center', width: '25%' }
        ]
      }
    }
  },
  computed: {
    // valueKey() {
    //   return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
    // },
    modalProps() {
      return Object.assign({
        valueKey: this.roleSettings.returnKeys[1],
        multiple: this.multiple,
        returnKeys: this.roleSettings.returnKeys,
        displayKey: this.roleSettings.returnKeys[1]
      }, this.$attrs)
    },
    positionModalProps() { // 职务弹窗属性
      return Object.assign({
        valueKey: this.positionSettings.returnKeys[1],
        multiple: this.multiple,
        returnKeys: this.positionSettings.returnKeys,
        displayKey: this.positionSettings.displayKey
      }, this.$attrs)
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    uploadClick() {
      this.$refs.files1.click()
    },
    uploadFile1(e) { // 读取本地bpmn文件
      const _this = this
      if (!e || !window.FileReader) return // 看支持不支持FileReader
      const reader = new FileReader()
      let name = e.target.files[0].name
      if (!name) {
        return false
      }
      name = name.split('.')
      if (name[name.length - 1] === 'bpmn') {
        reader.readAsText(e.target.files[0]) // 这里是最关键的一步，转换就在这里 （参数必须是blob对象）
        reader.onloadend = function() {
          _this.uploadFile = this.result
          _this.createNewDiagram()
          _this.$refs.files1.value = ''
        }
      } else {
        this.$message.error('请选择后缀为bpmn的文件')
      }
    },
    roleSelectOk(value) {
      this.setBpmnModelerProperties(value.join(','))
      this.selectValue = []
    },
    selectOK(rows, idstr) {
      this.setBpmnModelerProperties(idstr)
    },
    setBpmnModelerProperties(value) {
      const modeling = this.bpmnModeler.get('modeling')
      if (this.shape) {
        if (this.userSelect.key === 'multiInstance-collection') {
          let multiInstanceLoopCharacteristics = this.shape.businessObject.loopCharacteristics
          if (!multiInstanceLoopCharacteristics) {
            multiInstanceLoopCharacteristics = this.bpmnModeler.get('moddle').create('bpmn:MultiInstanceLoopCharacteristics')
          }
          multiInstanceLoopCharacteristics.collection = '${testUser.resolveUsersForTask(execution,"' + value + '")}'
          modeling.updateProperties(this.shape, {
            'loopCharacteristics': multiInstanceLoopCharacteristics
          })
        } else {
          modeling.updateProperties(this.shape, {
            [this.userSelect.key]: value
          })
        }
      }
    },
    async init() {
      this.$nextTick(() => {
        this.initBpmn()
      })
    },
    initBpmn() {
      // 获取到属性ref为“canvas”的dom节点
      const canvas = this.$refs.canvas
      // 建模
      this.bpmnModeler = new BpmnModeler({
        container: canvas,
        propertiesPanel: {
          parent: '#js-properties-panel' // 添加控制板
        },
        additionalModules: [
          // 右边的工具栏(固定引入)
          propertiesPanelModule,
          // 自定义右边工作栏的内容
          propertiesProviderModule,
          customTranslateModule
        ],
        moddleExtensions: {
          // camunda: camundaModdleDescriptor
          activiti: activitiModdleDescriptor
        }
      })
      this.createNewDiagram()
    },
    async createNewDiagram() {
      let bpmnXmlStr = ''
      if (this.uploadFile) {
        bpmnXmlStr = this.uploadFile
        // this.transformCanvas(bpmnXmlStr)
      } else if (this.xml === '') {
        bpmnXmlStr = this.defaultXmlStr
        // this.transformCanvas(bpmnXmlStr)
      } else {
        bpmnXmlStr = this.xml
        // this.transformCanvas(this.xml)
      }
      this.transformCanvas(bpmnXmlStr)
      const downloadLink = this.$refs.saveDiagram
      this.setEncoded(downloadLink, 'diagram.bpmn', bpmnXmlStr)
    },
    async transformCanvas(bpmnXmlStr) {
      // 将字符串转换成图显示出来
      try {
        const result = await this.bpmnModeler.importXML(bpmnXmlStr)
        const { warnings } = result
        if (warnings.length > 0) {
          console.error(warnings)
        } else {
          this.uploadFile ? this.uploadFile = null : ''
          this.success()
          var canvas = this.bpmnModeler.get('canvas')
          canvas.zoom('fit-viewport')
          // const allShapes = this.bpmnModeler.get('elementRegistry').getAll()
          // console.log(allShapes)
        }
      } catch (err) {
        console.log(err.message, err.warnings)
      }
    },
    success() {
      this.addModelerListener()
      this.addUserSelectListener()
      this.shape = this.bpmnModeler.get('canvas')._rootElement
      const modeling = this.bpmnModeler.get('modeling')
      if (this.shape) {
        modeling.updateProperties(this.shape, {
          id: this.processData.key,
          name: this.processData.name
        })
      }
      this.addBpmnListener()
    },
    addUserSelectListener(event) {
      this.$nextTick(() => {
        this.buttonsUserClick()
        this.buttonsMoreClick()
      })
    },
    buttonsUserClick() {
      const that = this
      const buttons = document.querySelectorAll('.user-role-select')
      if (buttons) {
        buttons.forEach((button, i) => {
          button.onclick = function() {
            var userId = ['assignee', 'candidateUsers', 'candidateStarterUsers', 'initiator', 'multiInstance-collection']
            // var roleId = ['candidateGroups', 'candidateStarterGroups']
            if (userId.indexOf(this.id) >= 0) {
              that.$refs.selectModal.showModal()
            } else {
              that.roleSettings.visible = true
              // that.positionSettings.positionVisible = true
            }
            that.userSelect.key = this.id
          }
        })
      }
    },
    buttonsMoreClick() {
      const buttons = document.querySelectorAll('.user-role-select-more')
      const that = this
      if (buttons) {
        buttons.forEach((button, i) => {
          button.onclick = function() {
            var userId = ['assignee-more']
            if (userId.indexOf(this.id) >= 0) {
              that.$refs.PositionAndExpressionModal.editModalVisible = true
            }
            that.userSelect.key = this.id.replace('-more', '')
          }
        })
      }
    },
    // // 添加绑定事件
    addBpmnListener() {
      const that = this
      const downloadLink = this.$refs.saveDiagram
      // 给图绑定事件，当图有发生改变就会触发这个事件
      this.bpmnModeler.on('commandStack.changed', function(e) {
        that.saveDiagram(function(xml, err) {
          that.setEncoded(downloadLink, 'diagram.bpmn', err ? null : xml)
        })
      })
    },
    addModelerListener() {
      this.bpmnModeler.on('element.click', (e) => { // 监听用户、角色选择按钮点击
        var elementRegistry = this.bpmnModeler.get('elementRegistry')
        this.shape = e.element ? elementRegistry.get(e.element.id) : e.shape
        const type = this.shape && this.shape.type
        if (type === 'bpmn:Process' || type === 'bpmn:UserTask' || type === 'bpmn:StartEvent' || type === 'bpmn:Participant') {
          this.addUserSelectListener(e)
        }
      })
      this.bpmnModeler.on('selection.changed', (e) => { // 监听用户、角色选择按钮点击
        this.shape = e.newSelection[0]
        const type = this.shape && this.shape.type
        if (type === 'bpmn:UserTask' || type === 'bpmn:StartEvent' || type === 'bpmn:Participant') {
          this.addUserSelectListener(e)
        }
      })
    },
    // 下载为bpmn格式,done是个函数，调用的时候传入的
    async saveDiagram(done) {
      try {
        const result = await this.bpmnModeler.saveXML({ format: true })
        const { xml } = result
        done(xml)
      } catch (err) {
        console.log(err)
      }
    },
    async getDiagramXml(done) {
      try {
        const result = await this.bpmnModeler.saveXML({ format: true })
        const result2 = await this.bpmnModeler.saveSVG()
        const { xml } = result
        const { svg } = result2
        return {
          xml,
          svg
        }
      } catch (err) {
        console.log(err)
      }
    },
    // 当图发生改变的时候会调用这个函数，这个data就是图的xml
    setEncoded(link, name, data) {
      // 把xml转换为URI，下载要用到的
      const encodedData = encodeURIComponent(data)
      // const xmlFile = new File([data], 'test.bpmn')
      if (data && link) {
        link.className = 'active'
        link.href = 'data:application/bpmn20-xml;charset=UTF-8,' + encodedData
        link.download = name
      }
    },
    handleUndo() { // 后退
      this.bpmnModeler.get('commandStack').undo()
    },
    handleRedo() { // 前进
      this.bpmnModeler.get('commandStack').redo()
    },
    handleZoomIn() {
      this.zoom += 0.1
      var canvas = this.bpmnModeler.get('canvas')
      canvas.zoom(this.zoom)
    },
    handleZoomOut() {
      this.zoom -= 0.1
      var canvas = this.bpmnModeler.get('canvas')
      canvas.zoom(this.zoom)
    },
    saveClick() {
      const that = this
      this.getDiagramXml().then(function(data) {
        that.$emit('ok', data)
      })
    },
    setInputDisable() {
      // if (this.shape && this.shape.type !== 'bpmn:Process') { // 根节点禁用编号和名称输入
      //   document.getElementById('activiti-name').setAttribute('contenteditable', 'true')
      //   document.getElementById('camunda-id').removeAttribute('disabled')
      //   document.querySelector('#camunda-id + .clear').removeAttribute('disabled')
      // } else {
      //   const root = this.bpmnModeler.get('canvas')._rootElement
      //   document.getElementById('activiti-name').setAttribute('contenteditable', 'false')
      //   document.getElementById('activiti-name').innerText = root.businessObject.name
      //   document.getElementById('camunda-id').setAttribute('disabled', 'disabled')
      //   document.querySelector('#camunda-id + .clear').setAttribute('disabled', 'disabled')
      // }
    },
    selectPositionAndExpression(values) {
      this.setBpmnModelerProperties(values)
    }
  }
}
</script>
<style >
.containers .djs-palette.two-column.open{
  width:100px !important;
}
</style>
<style scoped>
.loading {
    font-size: 26px;
}
.containers{
	background-color: #ffffff;
	width: 100%;
	height: calc(100vh - 52px);
}
.canvas{
	width: 100%;
	height: 100%;
}
.panel{
	position: absolute;
	right: 0;
	top: 0;
	width: 300px;
}
.tools_{
  display: inline-block;
  margin-left: 150px;
  margin-top:10px ;
  /* width: 200px; */
  background: #FAFAFA;
  border: solid 1px #CCC;
  border-radius: 2px;
  height: 40px;
  line-height: 40px;
}
.tools_ i{
  font-size: 18px;
  margin-left: 5px;
  margin-right: 5px;
  cursor: pointer;
}
.tools_ i:hover{
  color: rgb(255, 116, 0);
}
a{
  color: rgba(0, 0, 0, 0.65);
}
.containers{
  background: white;
  overflow: auto;
  background-image: linear-gradient(90deg, rgba(220, 220, 220, 0.5) 6%, transparent 0),
    linear-gradient(rgba(192, 192, 192, 0.5) 6%, transparent 0);
  background-size: 12px 12px;
  width: 100%;
  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
}
</style>
